# Copyright (c) 2007, Enrico Franchi
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the University of California, Berkeley nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from Foundation import *
from AppKit import *

from PyObjCTools import NibClassBuilder

import models
import package
import filters
import tasks
import notifications
import linguistics

import task_executor
from   app_logger    import AppLogger
from   linguistics   import AVAILABLE_TAG, INSTALLED_TAG, OUTDATED_TAG, INVALID_TAG
from   port_command  import targets

class iportAppDelegate(NibClassBuilder.AutoBaseClass):
    def init(self):
        self.selected_tab = AVAILABLE_TAG
        self.executor     = task_executor.TaskExecutor.sharedExecutor()
        self.executor.run()
        return self
    
    def awakeFromNib(self):
        self.initLogger()
        notifications.registerForPerformActionNotifications(self, 'logAction:')
        notifications.registerForErrorNotifications(self, 'showErrorMessage:')
        
    def applicationDidFinishLaunching_(self, notification):
        for tag in [AVAILABLE_TAG, INSTALLED_TAG, OUTDATED_TAG]:
            self.portsDataSource.refreshModel(tag)
        
    def tabView_didSelectTabViewItem_(self, tabView, item):
        self.selected_tab      = str(item.identifier())
        self.currentTableView  = getattr(self, self.selected_tab + 'TableView')
        self.drawer.close()
        if getattr(self.portsDataSource, self.selected_tab + '_void'):
            self.portsDataSource.refreshModel(self.selected_tab)
        self.refreshButtons()
        self.syncListWithFilter()
    
    def closeDrawer(self):
        self.drawer.close()
        
    def openDrawer(self):
        if self.drawer.state() == 0:
            self.drawer.open()
            
    def refreshButtons(self):
        if self.selected_tab == AVAILABLE_TAG:
            # this must be made more precise, we should be able to 
            # manage at this level packages that are already installed
            # if they are outdated etc etc etc
            self.installButton.setEnabled_(True)
            self.upgradeButton.setEnabled_(False)
            self.uninstallButton.setEnabled_(False)
        if self.selected_tab == INSTALLED_TAG:
            self.installButton.setEnabled_(False)
            self.upgradeButton.setEnabled_(False) # needs checking
            self.uninstallButton.setEnabled_(True)
        if self.selected_tab == OUTDATED_TAG:
            self.installButton.setEnabled_(False)
            self.upgradeButton.setEnabled_(True)
            self.uninstallButton.setEnabled_(True)
            
    def syncListWithFilter(self):
        string = self.searchField.stringValue().strip()
        if len(string) > 0:
            try:
                if self.regexCheckBox.state():
                    filter_ = filters.RegexFilter(string)
                else:
                    filter_ = filters.TextFilter(string)
                self.setValidSearchString()
                self.portsDataSource.setFilter(filter_, self.selected_tab)
            except filters.InvalidFilter:
                self.setInvalidSearchString()
        else:
            self.portsDataSource.setFilter(None, self.selected_tab)
        self.portsDataSource.refreshView(self.selected_tab)
        
    def controlTextDidChange_(self, notification):
        self.syncListWithFilter()
        
    def changedFilterType_(self, sender):
        self.syncListWithFilter()
    
    def setInvalidSearchString(self):
        self.searchField.setTextColor_(NSColor.redColor())
    
    def setValidSearchString(self):
        self.searchField.setTextColor_(NSColor.blackColor())
    
    def selectRowOfVisibleTableByName(self, name):
        tag = self.selected_tab
        index, row = getattr(self.portsDataSource, tag + '_model')[name]
        index_set = NSIndexSet.indexSetWithIndex_(index)
        self.currentTableView.selectRowIndexes_byExtendingSelection_(index_set, False)
        self.currentTableView.scrollRowToVisible_(index)
        self.window.makeFirstResponder_(self.currentTableView)
        
    def installPressed_(self, sender):
        self.performLongTask(targets.INSTALL)
        
    def uninstallPressed_(self, sender):
        self.performLongTask(targets.UNINSTALL)
        
    def upgradePressed_(self, sender):
        self.performLongTask(targets.UPGRADE)
        
    def selfUpgradePressed_(self, sender):
        print 'Self upgrade pressed.'
        
    def syncPressed_(self, sender):
        print 'Sync pressed.'
        
    def performLongTask(self, action):
        en = self.currentTableView.selectedRowEnumerator()
        for port in en:
            self.taskPerformingIndicator.startAnimation_(self)
            port_name = self.portsDataSource.portNameByRow(
                self.currentTableView, port
            )
            task = None
            if self.drawer.state() > 0 and action == targets.INSTALL: # there may be variants
                variants = self.portInfoController.selectedVariants()
                task = tasks.longActionFactory(action, port_name, variants=variants)
            elif action == targets.UNINSTALL:
                version = self.portsDataSource.portVersionByRow(
                     self.currentTableView, port
                )
                task = tasks.longActionFactory(action, port_name, version=version)
            else:
                task = tasks.longActionFactory(action, port_name)
            self.executor.addTask(task)
    
    def initLogger(self):
        import sys
        sys.stdout = AppLogger.sharedLogger()
    
    def logAction_(self, notification):
        # add here the refreshing of views when necessary
        ui          = notification.userInfo()
        action      = ui['action']
        port_name   = ui['port']
        stage       = ui['stage']
        status_text = 'Package %s %s.' % (port_name, linguistics.past_actions[stage])
        task_number = len(self.executor)
        if task_number > 0:
            number_text = '%i tasks remaining.' % (task_number - 1)
            self.taskPerformingIndicator.startAnimation_(self)
        else:
            number_text = 'Completed all tasks.'
            self.taskPerformingIndicator.stopAnimation_(self)
        self.tasksRemainingLabel.setStringValue_(number_text)
        self.taskStatusLabel.setStringValue_(status_text)
        
        print "During %s, %s is at stage %s." % (action, port_name, stage)
    
    def windowShouldClose_(self, sender):
        return 0

    def showErrorMessage_(self, notification):
        ui    = notification.userInfo()
        title = ui.get('title') or u'Error'
        msg   = ui['message']
        NSRunAlertPanel(title, msg, 'Ok', None, None)

        
